package ihm;

import ihm.panel.BMPCapture;
import ihm.panel.BMPPlayer;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;

import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;

import cipher.algorithm.A51;
import cipher.algorithm.Algorithm;
import cipher.algorithm.Empty;
import cipher.algorithm.Lfsr;
import cipher.module.Cipher_;

import network.KrakenClient;
import network.KrakenClientMsg;
import network.KrakenServer;

public class MainFrame extends JFrame implements ActionListener {
	
	private static final long serialVersionUID = 1L;
	private JPanel image_jp, control_jp, label_jp;
	private JTextField url_jtf, ip_jtf, port_jtf, key1_jtf, key2_jtf, key_size_jl;
	private JComboBox<String> side_jcb, algo_jcb, mode_jcb; // <JPanel> ?
	private JButton connect_jb, stop_jb, refresh_jb, key_jb, sendKey_jb;
	private JLabel url_jl, ip_jl, key1_jl, key2_jl, side_jl, algo_jl;
	private ArrayList<String> init_key;

	public static final Dimension IMAGE_SIZE = new Dimension(320, 240);
	public static final Dimension COMPONENT_SIZE_1 = new Dimension(300, 25);
	public static final Dimension COMPONENT_SIZE_2 = new Dimension(148, 25);
	public static final Dimension COMPONENT_SIZE_3 = new Dimension(98, 25);
	
	private KrakenServer serveur;
	private KrakenClient client;
	
	public MainFrame() {
		setLayout(new BorderLayout());
		init_key = new ArrayList<String>();
		setTitle("Projet Cryptographie - Demo");
		
		addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
				// TODO faire le necessaire pour fermer proprement la jmf et la connexion
				if(client != null)
					client.sendMessage_noAnwser(KrakenClientMsg.CLIENT_EXIT.toString());
				System.out.println("exit");
				System.exit(0);
			}
		});

		image_jp = new JPanel();
		//image_jp = new BMPCapture();
		image_jp.setPreferredSize(IMAGE_SIZE);
		
		label_jp = new JPanel();
		label_jp.setLayout(new GridBagLayout());
		GridBagConstraints constraints = new GridBagConstraints();
		constraints.insets = new Insets(4, 3, 8, 3);
		
		constraints.gridx = 0;
		constraints.gridy = 0;
		constraints.gridwidth = GridBagConstraints.NONE;
		constraints.anchor = GridBagConstraints.EAST;
		constraints.fill = GridBagConstraints.NONE;

		// Labels
		url_jl = new JLabel("Entrée :");
		constraints.gridy++;
		label_jp.add(url_jl, constraints);
		
		ip_jl = new JLabel("IP/PORT :");
		constraints.gridy++;
		label_jp.add(ip_jl, constraints);
		
		key1_jl = new JLabel("Clé 1 :");
		constraints.gridy++;
		label_jp.add(key1_jl, constraints);
		
		key2_jl = new JLabel("Clé 2 :");
		constraints.gridy++;
		label_jp.add(key2_jl, constraints);
		
		side_jl = new JLabel("Rôle :");
		constraints.gridy++;
		label_jp.add(side_jl, constraints);
		
		algo_jl = new JLabel("Algo/Mode :");
		constraints.gridy++;
		label_jp.add(algo_jl, constraints);

		JLabel tmp_jl = new JLabel();
		constraints.gridy++;
		constraints.insets = new Insets(0, 3, 55, 3);
		//tmp_jl.setPreferredSize(COMPONENT_SIZE_3);
		label_jp.add(tmp_jl, constraints);
		
		control_jp = new JPanel();
		control_jp.setLayout(new GridBagLayout());
		constraints = new GridBagConstraints();
		constraints.insets = new Insets(0, 3, 3, 3);
		
		constraints.gridx = 0;
		constraints.gridy = 0;
		constraints.gridwidth = GridBagConstraints.NONE;
		constraints.anchor = GridBagConstraints.WEST;
		constraints.fill = GridBagConstraints.NONE;
		
		
		// URL
		url_jtf = new JTextField("vfw://0");
		url_jtf.setPreferredSize(COMPONENT_SIZE_1);
		constraints.gridy++;
		control_jp.add(url_jtf, constraints);
		
		// Address IP
		ip_jtf = new JTextField("127.0.0.1");
		ip_jtf.setPreferredSize(new Dimension(198, 25));
		constraints.gridy++;
		constraints.gridwidth = GridBagConstraints.NONE;
		constraints.anchor = GridBagConstraints.WEST;
		control_jp.add(ip_jtf, constraints);
		
		port_jtf = new JTextField("18000");
		port_jtf.setPreferredSize(new Dimension(98, 25));
		constraints.gridx++;
		constraints.gridwidth = GridBagConstraints.REMAINDER;
		constraints.anchor = GridBagConstraints.EAST;
		control_jp.add(port_jtf, constraints);
		
		// Key1
		key1_jtf = new JTextField("1110101010010010011011000000111010011010001110011111100001011000");
		key1_jtf.setPreferredSize(COMPONENT_SIZE_1);
		constraints.gridx--;
		constraints.gridy++;
		control_jp.add(key1_jtf, constraints);
		
		// Key2
		key2_jtf = new JTextField("1010100011000111010101");
		key2_jtf.setPreferredSize(COMPONENT_SIZE_1);
		constraints.gridy++;
		control_jp.add(key2_jtf, constraints);

		// Roles
		String[] side_list = {"client", "serveur"};
		side_jcb = new JComboBox<String>(side_list);
		side_jcb.setPreferredSize(COMPONENT_SIZE_1);
		constraints.gridy++;
		control_jp.add(side_jcb, constraints);
		
		
		// Algorithms
		String[] algo_list = {"Zéro", "LFSR", "A5/1"};
		algo_jcb = new JComboBox<String>(algo_list);
		algo_jcb.setPreferredSize(COMPONENT_SIZE_2);
		constraints.gridy++;
		constraints.gridwidth = GridBagConstraints.NONE;
		constraints.anchor = GridBagConstraints.WEST;
		control_jp.add(algo_jcb, constraints);
		
		String[] mode_list = {"ECB", "CTR"};
		mode_jcb = new JComboBox<String>(mode_list);
		mode_jcb.setPreferredSize(COMPONENT_SIZE_2);
		constraints.gridx++;
		constraints.gridwidth = GridBagConstraints.REMAINDER;
		constraints.anchor = GridBagConstraints.EAST;
		control_jp.add(mode_jcb, constraints);

		
		// Buttons
		connect_jb = new JButton("Connexion");
		connect_jb.setPreferredSize(COMPONENT_SIZE_3);
		connect_jb.addActionListener(this);
		constraints.gridx--;
		constraints.gridy++;
		constraints.gridwidth = GridBagConstraints.NONE;
		constraints.anchor = GridBagConstraints.WEST;
		control_jp.add(connect_jb, constraints);

		stop_jb = new JButton("Arrêter");
		stop_jb.setPreferredSize(COMPONENT_SIZE_3);
		stop_jb.addActionListener(this);
		stop_jb.setEnabled(false);
		constraints.gridx++;
		constraints.anchor = GridBagConstraints.CENTER;
		control_jp.add(stop_jb, constraints);
		
		refresh_jb = new JButton("Rafraichir");
		refresh_jb.setPreferredSize(COMPONENT_SIZE_3);
		refresh_jb.addActionListener(this);
		refresh_jb.setEnabled(false);
		constraints.gridx++;
		constraints.gridwidth = GridBagConstraints.REMAINDER;
		constraints.anchor = GridBagConstraints.EAST;
		control_jp.add(refresh_jb, constraints);
		
		// Key size
		key_jb = new JButton("Générer clé");
		key_jb.setPreferredSize(new Dimension(124, 25));
		key_jb.addActionListener(this);
		constraints.gridy++;
		constraints.gridwidth = GridBagConstraints.NONE;
		constraints.anchor = GridBagConstraints.WEST;
		control_jp.add(key_jb, constraints);
		
		key_size_jl = new JTextField("16");
		key_size_jl.setPreferredSize(new Dimension(48, 25));
		constraints.gridx++;
		constraints.gridwidth = GridBagConstraints.REMAINDER;
		constraints.anchor = GridBagConstraints.CENTER;
		control_jp.add(key_size_jl, constraints);
		
		// Send key
		sendKey_jb = new JButton("Envoyer clé");
		sendKey_jb.setPreferredSize(new Dimension(124, 25));
		sendKey_jb.addActionListener(this);
		constraints.gridx++;
		constraints.gridwidth = GridBagConstraints.NONE;
		constraints.anchor = GridBagConstraints.EAST;
		control_jp.add(sendKey_jb, constraints);	

		JPanel tmp = new JPanel();
		tmp.setLayout(new BorderLayout());
		tmp.add(label_jp, BorderLayout.WEST);
		tmp.add(control_jp, BorderLayout.EAST);
		
		add(image_jp, BorderLayout.CENTER);
		add(tmp, BorderLayout.EAST);

		pack();
		setVisible(true);
	}
	
	public void connect(boolean perm) {
		url_jtf.setEnabled(!perm);
		ip_jtf.setEnabled(!perm);
		port_jtf.setEnabled(!perm);
		side_jcb.setEnabled(!perm);
		connect_jb.setEnabled(!perm);

		stop_jb.setEnabled(perm);
		refresh_jb.setEnabled(perm);
	}
	
	@Override
	public void actionPerformed(ActionEvent e) {
		Object source = e.getSource();
		
		if(source.equals(connect_jb)) {
			remove(image_jp);
			
			// démarrage en tant que client
			if(side_jcb.getSelectedItem().equals("client")) {
				image_jp = new BMPPlayer();
				add(image_jp, BorderLayout.CENTER);
				
				client = new KrakenClient(ip_jtf.getText(), (BMPPlayer) image_jp, (MainFrame) this);
				client.start();
			}
			
			// démarrage en tant que serveur
			else {
				image_jp = new BMPCapture(url_jtf.getText());
				add(image_jp, BorderLayout.CENTER);
				
				if(serveur == null) {
					serveur = new KrakenServer((MainFrame) this, (BMPCapture) image_jp, port_jtf.getText());
					refreshServerInfos();
					serveur.start();
				}
				else
					System.out.println("Serveur déjà démarré!");
			}
			
			connect(true);
			revalidate();
		}
		
		else if(source.equals(stop_jb)) {
			// arret en tant que client
			if(side_jcb.getSelectedItem().equals("client")) {
				
			}
			
			// arret en tant que serveur
			else {
				((BMPCapture) image_jp).stopPlayer();
			}
			remove(image_jp);
			
			connect(false);
			revalidate();
		}
		
		//boutton key
		else if(source.equals(key_jb)){
			//"Zéro", "LFSR", "A5/1"
			Algorithm algo;
			if("LFSR".equalsIgnoreCase((String)algo_jcb.getSelectedItem())){
				algo = new Lfsr();
				this.init_key = ((Algorithm)algo).generateNewKey(Integer.valueOf(key_size_jl.getText()));
				this.key1_jtf.setText(init_key.get(0));
				this.key2_jtf.setText(init_key.get(1));
			}
			else if("A5/1".equalsIgnoreCase((String)algo_jcb.getSelectedItem())){
				algo = new A51();
				this.init_key = ((Algorithm)algo).generateNewKey(64);
				this.key1_jtf.setText(init_key.get(0));
				this.key2_jtf.setText(init_key.get(1));
			}
			else if("Zéro".equalsIgnoreCase((String)algo_jcb.getSelectedItem())){
				this.key1_jtf.setText("");
				this.key2_jtf.setText("");
			}
		}
		
		else if(source.equals(sendKey_jb)){
			//this.serveur.setSendKey(true);
			
		}
		
		// boutton refresh
		else {
			// coté serveur
			if(side_jcb.getSelectedItem().equals("serveur"))
				refreshServerInfos();
			// coté client
			else
				client.sendMessage_noAnwser(KrakenClientMsg.END_CLIENT.toString());
		}
	}
	
	public Algorithm initAlgo() {
		Algorithm algo;
		String key = key1_jtf.getText();
		String key2 = key2_jtf.getText();
		
		if(algo_jcb.getSelectedItem().equals("LFSR")) {
			if(!key.matches("[01]{1,31}") || !key2.matches("[01]{1,31}")) {
				JOptionPane.showMessageDialog(this,
						"Les clés ne sont pas au bon format!\n" +
						"Entrez un nombre binaire entre 1 et 31 bits!",
						"Les clés ne sont pas au bon format!",
						JOptionPane.ERROR_MESSAGE);
				algo = new Empty();
			}
			else
				algo = new Lfsr(key1_jtf.getText().length(), key1_jtf.getText(), key2_jtf.getText());
		}
		else if(algo_jcb.getSelectedItem().equals("A5/1")) {
			if(!key.matches("[01]{64}") || !key2.matches("[01]{22}")) {
				JOptionPane.showMessageDialog(this,
						"Les clés ne sont pas au bon format!\n" +
						"Entrez un nombre binaire sur 64 bits et le compteur sur 22 bits!",
						"Les clés ne sont pas au bon format!",
						JOptionPane.ERROR_MESSAGE);
				algo = new Empty();
			}
			else
				algo = new A51(key1_jtf.getText(), key2_jtf.getText());
		}
		else
			algo = new Empty();
		
		return algo;
	}
	
	public void refreshClientInfos() {
		System.out.println("reinitialise le client");
		
		if(mode_jcb.getSelectedItem().equals("ECB"))
			client.setMODE(Cipher_.ECB);
		else
			client.setMODE(Cipher_.CTR);
		
		client.setAlgo(initAlgo());
	}
	
	public void refreshServerInfos() {
		System.out.println("reinitialise le serveur");
		
		if(mode_jcb.getSelectedItem().equals("ECB"))
			serveur.setMODE(Cipher_.ECB);
		else
			serveur.setMODE(Cipher_.CTR);
		
		serveur.setAlgo(initAlgo());
	}
	
	public ArrayList<String> getInitKey(){	return this.init_key;	}
	public JTextField getKeyField1(){	return this.key1_jtf;	}
	public JTextField getKeyField2(){	return this.key2_jtf;	}
	
	public static void main(String[] args) {
		new MainFrame();
	}

}
